package com.gcloud.mesh.analysis.ml;

import java.math.BigDecimal;
import java.math.RoundingMode;

import com.gcloud.mesh.header.vo.analysis.DataVo;

public class SecondOrderPolynomialOptimizer implements IFitterOptimizer {
	
	private BigDecimal a = BigDecimal.ONE;
    private BigDecimal b = BigDecimal.ZERO;
    private BigDecimal c = BigDecimal.ZERO;
    
    public static void main(String[] args) {
    	double[] factors = new double[] {2.4753368200833683, -0.01022928870292641,1.994421199441637E-5};
    	SecondOrderPolynomialOptimizer optimizer = new SecondOrderPolynomialOptimizer(factors);
    	Point p = optimizer.goldSearch(100, 400, 1, 1, 0.01);
    	System.out.println(p.getX().doubleValue());
    	System.out.println(p.getY().doubleValue());
    }
	
	public SecondOrderPolynomialOptimizer(double[] factors) {
		
		this.a = new BigDecimal(factors[2]);
		this.b = new BigDecimal(factors[1]);
		this.c = new BigDecimal(factors[0]);
		//①得到抛物线顶点
		//②获取对称轴
	}
	
	/**
	 * 获取最优值点
	 */
	public DataVo getOptimum() {
		Point p = goldSearch(100, 400, 1, 1, 0.1);
		DataVo vo = new DataVo();
//		vo.setDataX(191.6);
//		vo.setDataY(1.006);
		vo.setDataX(p.getX().doubleValue());
		vo.setDataY(p.getY().doubleValue());
		return vo;
	}
	
	/**
	 * 判断点是否在曲线上
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean isOnline(double x,double y){
        BigDecimal dependent = new BigDecimal(y);
        BigDecimal square = new BigDecimal(Math.pow(2, x));
        BigDecimal independent = new BigDecimal(x);
        return dependent.equals(square.multiply(this.getA()).add(independent.multiply(this.getB())).add(this.getC()));
    }
	
	/**
	 * 获取对称轴
	 * @return
	 */
    public BigDecimal getAxisOfSymmetry(){
        return this.getB().divide(this.getA().multiply(new BigDecimal(2)),2, RoundingMode.HALF_UP).negate();
    }
    
    /**
     * 获取最值点
     * @return
     */
    public Point getMaxValue(){
    	Point point = new Point();
    	BigDecimal x = getAxisOfSymmetry();
        BigDecimal subtract = new BigDecimal(4).multiply(this.getA()).multiply(this.getC()).subtract(new BigDecimal(Math.pow(2, this.getB().doubleValue())));
        BigDecimal y = subtract.divide(new BigDecimal(4).multiply(this.getA()),2,RoundingMode.HALF_UP);
        point.setX(x);
        point.setY(y);
        return point;
    }
    
    public double getValue(double x) {
    	BigDecimal dependent = new BigDecimal(x);
    	BigDecimal value = a.multiply(dependent.pow(2)).add(b.multiply(dependent)).add(c);
    	return value.doubleValue();
    }
	
	public BigDecimal getA() {
		return this.a;
	}
	
	public BigDecimal getB() {
		return this.b;
	}
	
	public BigDecimal getC() {
		return this.c;
	}
	
	/**
	 * 黄金分割法求最小值
	 * @param start  区间起始值
	 * @param end  区间终止值
	 * @param eps  允许区间误差
	 * @param sigma  最小值允许误差
	 * @return
	 */
	private Point goldSearch(double start, double end, double min, double eps, double sigma) {
		
		double a, b;
		a = Math.min(start, end);
		b = Math.max(start, end);
		
		double t1, t2, f1, f2;
		double ranta = 0.618;
		t1 = a + ranta*(b-a);
		t2 = b - ranta*(b-a);
		f1 = this.getValue(t1);
		f2 = this.getValue(t2);
		while(t2-t1 > eps) {
			if(Math.abs(f1 - min) < sigma) {
				return new Point(t1, f1);
			}else if(Math.abs(f2 - min) < sigma) {
				return new Point(t2, f2);
			}
			if(f1<=f2) {
				b = t2;
			}else {
				a = t1;
			}
			t1 = a + ranta*(b-a);
			t2 = b - ranta*(b-a);
			f1 = this.getValue(t1);
			f2 = this.getValue(t2);
		}
		Point p = new Point();
		if(f1 > f2) {
			p.setX(new BigDecimal(t2));
			p.setY(new BigDecimal(f2));
		}else {
			p.setX(new BigDecimal(t1));
			p.setY(new BigDecimal(f2));
		}
		return p;
	}

}
